package com.lcy.lambda.function;

import com.lcy.lambda.data.Author;

import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.lcy.lambda.data.Author.getAuthors;

/**
 * 方法引用就是把已经有的方法拿过来用，当做函数式接口中抽象方法的方法体
 * 前提：
 *  ①需要有函数式接口
 *
 *  ②被引用的方法必须已经存在
 *
 *  ③被引用方法的形参和返回值需要跟抽象方法保持一致
 *
 *  ④被引用方法的功能要满足当前的需求
 *
 *  基本格式：类名或者对象名::方法名
 *
 * @author lcy
 * @date 2024/10/12 17:49
 */
public class 方法引用 {

    /**
     * 使用前提
     * 1.如果我们在重写方法的时候，方法体中只有一行代码，
     * 2.并且这行代码是调用了某个类的静态方法，
     * 3.并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个静态方法中，
     * 这个时候我们就可以引用类的静态方法。
     */
    public void 引用类的静态方法(){
        List<Author> authors = getAuthors();
        List<String> collect = authors.stream()
                .map(author -> author.getAge())
                .map(age -> String.valueOf(age))
                .collect(Collectors.toList());

        //============方法引用=================
        /*
         *   理解第三前提：  .map(age -> String.valueOf(age))  此处匿名表达式为
         *  .map(new Function<Integer, String>() {
         *                     @Override
         *                     public String apply(Integer age) {
         *                         return String.valueOf(age);
         *                     }
         *                 })
         * 重写的是apply(Integer age)方法，该方法的参数是age，  String.valueOf(age)也是age，是按顺序写的
         * 假如重写的某个匿名内部类的方法参数是两个example（param1, param2）
         * 我们要去调用某个类的类方法也是两个参数。 Class1.function(param1, param2);
         * 就可以写成Class1::function
         * 但是！  如果Class.function(param2, param1).  顺序和参数不一样，就不能用方法引用了
         *
         */
        List<String> collect1 = authors.stream()
                .map(author -> author.getAge())
                .map(String::valueOf)
                .collect(Collectors.toList());
    }

    /**
     * 使用前提
     * 1.如果我们在重写方法的时候，方法体中只有一行代码，
     * 2.并且这行代码是调用了某个对象的成员方法，
     * 3.并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个成员方法中，
     * 这个时候我们就可以引用对象的实例方法
     */
    public void 引用对象的实例方法(){
        List<Author> authors = getAuthors();

        Stream<Author> authorStream = authors.stream();
        StringBuilder sb = new StringBuilder();
        authorStream.map(author -> author.getName())
                .forEach(name -> sb.append(name));

        //============方法引用=================
        /*
         *   理解第三前提：  .forEach(name -> sb.append(name));
         *   此处匿名表达式为
         *  .forEach(new Consumer<String>() {
                    @Override
                    public void accept(String name) {
                        sb.append(name);
                    }
                })
         * 重写的是accept(String name)方法，该方法的参数是name， 成员变量sb的append方法参数也是name，是按顺序写的
         * 假如重写的某个匿名内部类的方法参数是两个example（name1, name2）
         * 我们要去调用某个成员变量的的方法也是两个参数。 sb2.append2(name1, name2);
         * 就可以写成sb::function
         * 但是！  如果sb2.append2(name2, name1).  顺序和参数不一样，就不能用方法引用了
         *
         */
        authorStream.map(author -> author.getName())
                .forEach(sb::append);
    }


    interface UseString{
        String use(String str,int start,int length);
    }
    /**
     * 使用前提
     * 1如果我们在重写方法的时候，方法体中只有一行代码，
     * 2并且这行代码是调用了“第一个参数”的成员方法，
     * 3并且我们把要重写的抽象方法中剩余的所有的参数都按照顺序传入了这个成员方法中，
     * 这个时候我们就可以引用类的实例方法。
     */
    public static String 引用类的实例方法(String str, UseString useString){
        int start = 0;
        int length = 1;
        return useString.use(str,start,length);
    }

    /**
     * 格式：类名::new
     * 使用前提
     * 1如果我们在重写方法的时候，方法体中只有一行代码，
     * 2并且这行代码是调用了某个类的构造方法
     * 3并且我们把要重写的抽象方法中的所有的参数都按照顺序传入了这个构造方法中，
     * 这个时候我们就可以引用类的实例方法。
     */
    public void 构造器引用(){
        List<Author> authors = getAuthors();
        authors.stream()
                .map(author -> author.getName())
                .map(new Function<String, StringBuilder>() {
                    @Override
                    public StringBuilder apply(String name) {
                        return new StringBuilder(name);
                    }
                })
                .map(sb->sb.append("-三更").toString())
                .forEach(str-> System.out.println(str));

        //============方法引用=================
        /*
         *   理解第三前提：  .forEach(name -> sb.append(name));
         *   此处匿名表达式为
         *  .forEach(new Consumer<String>() {
                    @Override
                    public void accept(String name) {
                        sb.append(name);
                    }
                })
         * 重写的是accept(String name)方法，该方法的参数是name， 成员变量sb的append方法参数也是name，是按顺序写的
         * 假如重写的某个匿名内部类的方法参数是两个example（name1, name2）
         * 我们要去调用某个成员变量的的方法也是两个参数。 sb2.append2(name1, name2);
         * 就可以写成sb::function
         * 但是！  如果sb2.append2(name2, name1).  顺序和参数不一样，就不能用方法引用了
         *
         */
        authors.stream()
                .map(author -> author.getName())
                .map(StringBuilder::new)
                .map(sb->sb.append("-三更").toString())
                .forEach(str-> System.out.println(str));
    }


    public static void main(String[] args) {
        方法引用 方法引用 = new 方法引用();
        方法引用.引用类的静态方法();
        方法引用.引用对象的实例方法();



        引用类的实例方法("三更草堂", new UseString() {
            @Override
            public String use(String str, int start, int length) {
                return str.substring(start,length);
            }
        });
        //str,start,length 都是按顺序调用的，
        引用类的实例方法("三更草堂", String::substring);
    }
}
